/*------------------------------------------------------------------------------
 * Copyright 2012 NCR Corporation
 *------------------------------------------------------------------------------
 * All revision information is updated automatically from source code control
 * change records - please do not manually edit.
 *------------------------------------------------------------------------------
 *
 * $LastChangedRevision:: 2026                                     $
 * $LastChangedBy:: rb250096                                       $
 * $LastChangedDate:: 2012-04-07 18:34:30 -0400 (Sat, 07 Apr 2012) $
 */

package com.ncr.nep.dashboardcore.rest.client;

import java.util.logging.Logger;

import com.google.web.bindery.autobean.shared.AutoBeanCodex;
import com.google.web.bindery.autobean.shared.AutoBeanFactory;

/**
 * Callback object that is notified of success or failure of a REST-ful request
 * to a URL that returns a JSON document.
 *
 * <p>
 *     This class facilitates automatic de-serialization of the JSON into an
 *     Object via GWT's AutoBean mechanism, and as such requires an
 *     <code>AutoBeanFactory</code> instance in order to perform marshaling.
 * </p>
 *
 * @author rb250096
 * @version $Revision: 2026 $
 */
public abstract class JSONCallback<T> extends TextCallback {
    
    //--------------------------------------------------------------------------
    //
    //  Fields
    //
    //--------------------------------------------------------------------------

    /**
     * Logging instance.
     */
    private static Logger log = Logger.getLogger("JSONCallback");

    /**
     * Factory instance used to return an
     * {@link com.google.web.bindery.autobean.shared.AutoBean} instance wrapping
     * <code>clazz</code>.
     */
    protected AutoBeanFactory factory;

    /**
     * Class being de-serialized from the JSON string returned from the 
     * server in the HTTP response.
     */
    protected Class<T> clazz;

    /**
     * Indicates that the callback should not check the Content-Type returned
     * in the response to make sure it is <code>application/json</code>.  This
     * is useful when doing prototyping or testing.
     */
    protected boolean ignoreContentType;

    //--------------------------------------------------------------------------
    //
    //  Constructors
    //
    //--------------------------------------------------------------------------

    /**
     * Constructor
     * 
     * @param factory Factory instance used to return an
     *                {@link com.google.web.bindery.autobean.shared.AutoBean}
     *                instance wrapping clazz.
     * @param clazz Class being de-serialized from the JSON string returned from
     *              the server in the HTTP response.
     */
    public JSONCallback(AutoBeanFactory factory, Class<T> clazz) {
        super();

        this.factory = factory;
        this.clazz = clazz;
    }

    /**
     * Constructor
     *
     * @param factory Factory instance used to return an
     *                {@link com.google.web.bindery.autobean.shared.AutoBean}
     *                instance wrapping clazz.
     * @param clazz Class being de-serialized from the JSON string returned from
     *              the server in the HTTP response.
     * @param ignoreContentType True to ignore content type checking in response
     *                          handler.
     */
    public JSONCallback(AutoBeanFactory factory,
                        Class<T> clazz,
                        boolean ignoreContentType) {
        this(factory, clazz);
        this.ignoreContentType = ignoreContentType;
    }

    //--------------------------------------------------------------------------
    //
    //  Methods
    //
    //--------------------------------------------------------------------------

    /**
     * Callback method invoked when the remote request associated with the
     * callback returns successfully.
     *
     * @param result Object instance marshaled from JSON returned from server
     *               call.
     */
    public abstract void onSuccess(T result);

    /**
     * Callback method invoked when the remote request associated with the
     * callback fails due to an error.
     *
     * @param throwable Throwable with details on the error that occurred.
     * @param statusCode The HTTP status code returned in the server response
     *                   indicating the category of error that occurred.
     */
    public abstract void onFailure(Throwable throwable, int statusCode);

    @Override
    public void onSuccess(String result) {
        String contentType = (response != null) ? this.response.getHeader("Content-Type") : null;

        if((contentType != null && contentType.equals(MIMEType.APPLICATION_JSON.toString()))
                || this.ignoreContentType) {
            try {
                T obj = AutoBeanCodex.decode(this.factory, this.clazz,
                        result).as();
                this.onSuccess(obj);
            }
            catch(RuntimeException re) {
                this.onFailure(new JSONCallbackException("The JSON in " +
                        "the response could not be parsed.",
                        result, re),
                        this.response != null ? this.response.getStatusCode() : 0);
            }
        }
        else {
            this.onFailure(new JSONCallbackException("The response's " +
                    "Content-Type was not set to 'application/json'."),
                    this.response.getStatusCode());
        }
    }
}
